/**
 * Copyright (c) 2016-2020 https://github.com/zhaohuatai
 *
 * contact z_huatai@qq.com
 *  
 */

package org.zfes.snowy.core.util;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.zfes.snowy.core.data.DataSet;

import com.esotericsoftware.reflectasm.MethodAccess;
public class ZBeanUtil extends org.springframework.beans.BeanUtils{
	
	private static Map<String,MethodAccess> beanMethodcache=new HashMap<String,MethodAccess>();
	
	private static int beanMethodcacheCapacity=1000;
			
	private static MethodAccess getFromBeanMethodcache(Class<?> type){
		if(beanMethodcache==null){
			beanMethodcache=new HashMap<String,MethodAccess>();
			return null;
		}
		String key=type.getName();
		MethodAccess access=beanMethodcache.get(key);
		if(access==null){
			access = MethodAccess.get(type);
			if(access!=null){
				if(beanMethodcache.size()<=beanMethodcacheCapacity){
					beanMethodcache.put(key, access);
				}
			}
		}
		return access;
	}
	
	 
//	public static boolean isEmptyValue(Object value){
//		if(value==null){
//			return true;
//		}else if(value instanceof String){
//			return "".equals(String.valueOf(value).trim());
//		}else if(value instanceof Collection){
//			return ((Collection<?>)value).size()==0;
//		}else if(value.getClass().isArray()){
//			boolean b=false;
//			try{
//				b=((Collection[])value).length==0;
//			}catch(Exception e){
//				b=Arrays.asList(value).size()==0;
//			}
//			return b;
//		}else{
//			return false;
//		}
//	}


	
	public static void copy(Object source, Object target,Boolean ignorNull)
			throws BeansException{
		copy(source, target,ignorNull, null, (String[]) null);
	}
	
	private static void copy(Object source, Object target,Boolean ignorNull, Class<?> editable, String... ignoreProperties)
			throws BeansException {

		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");

		Class<?> actualEditable = target.getClass();
		if (editable != null) {
			if (!editable.isInstance(target)) {
				throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
						"] not assignable to Editable class [" + editable.getName() + "]");
			}
			actualEditable = editable;
		}
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

		for (PropertyDescriptor targetPd : targetPds) {
			Method writeMethod = targetPd.getWriteMethod();
			if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null) {
					Method readMethod = sourcePd.getReadMethod();
					if (readMethod != null &&
							ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
						try {
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							Object value = readMethod.invoke(source);
							if(ignorNull!=null&&ignorNull){
								//if(value!=null){//空对象 或者空集合
								if(value!=null&&(!"[]".equals(value.toString()))){//空对象 或者空集合
									if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
										writeMethod.setAccessible(true);
									}
									writeMethod.invoke(target, value);
								}
							}else{
								if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
									writeMethod.setAccessible(true);
								}
								writeMethod.invoke(target, value);
							}
							
						}
						catch (Throwable ex) {
							throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);
						}
					}
				}
			}
		}
	}
	@Deprecated
	@SuppressWarnings("unchecked")
	public static List<?> removeDuplicate(List<?> list) {
		@SuppressWarnings("rawtypes")
		List<?> listWithoutDup = new ArrayList(new HashSet(list));
		return listWithoutDup;
	}
	

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static List<?> removeDuplicateWithOrder(List list) {
		if(list==null||list.size()==0){
			return list;
		}
		List newList = new ArrayList();
		for(Object object:list){
			if(!newList.contains(object)){
				newList.add(object);
			}
		}
        return newList;
    }
	

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Object[] removeDuplicateWithOrder(Object[] array) {
		if(array==null||array.length==0){
			return array;
		}
		List newList = new ArrayList();
		for (Object object : array) {
			if (!newList.contains(object)) {
				newList.add(object);
			}
		}
		Class elementType = array.getClass().getComponentType();
		Object[] newArray = (Object[]) java.lang.reflect.Array.newInstance(elementType, 0);
		array = newList.toArray(newArray);
		return array;
    }
	@SuppressWarnings("rawtypes")
	public static Object convertMapToBean(Map map, Class<?> type) {
		Object obj = null;
		try{
			BeanInfo beanInfo = null;
			beanInfo = Introspector.getBeanInfo(type);
			obj = type.newInstance();
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String properName = property.getName();
				if (map.containsKey(property.getName())) {
					try {// 不影响其他属性
						Object value = map.get(properName);
						Method setter = property.getWriteMethod();  
						setter.invoke(obj, value);  
					} catch (Exception e) {
						//e.printStackTrace();
					}
	
				}
			}
		}catch(Exception e){
			e.printStackTrace();
			return obj;
		}
		return obj;
	}
	//jdk:
	//10000000次4089 4114 4113 4092 4157 4103 4163 4186 4196 4211 4160 4121 
    //1000000次 624 461 443 435 413 421 400 426 409 400 417 404 
	//100000次229 84 64 47 43 51 39 35 37 43 54 49 
	//10000次 91 33 29 22 15 12 5 6 18 9 33 9  
    //1000次 47 7 3 4 8 2 1 3 5 3 3 3 
	//-------------------------------------------------------
	//reflectasm
	//10000000次 5065 4849 4940 4818 4934 4784 4773 4739 4814 4827 4793 4774 
    //1000000次 713 510 529 454 458 468 451 463 450 472 460 455 
	//100000次218 92 76 50 49 61 45 47 42 56 64 59 
	//10000次 85 28 25 27 20 21 7 6 20 13 28 15 
	//1000次47 4 3 6 3 2 2 3 3 8 3 2
	@SuppressWarnings("rawtypes")
	public static Object convertMapToBeanByRefAsm(Map map, Class<?> type) {
		Object obj = null;
		try{
			BeanInfo beanInfo = null;
			MethodAccess access = getFromBeanMethodcache(type);
			beanInfo = Introspector.getBeanInfo(type);
			obj = type.newInstance();
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String properName = property.getName();
				if (map.containsKey(property.getName())) {
					try {// 不影响其他属性
						Object value = map.get(properName);
						Method setter = property.getWriteMethod();  
						access.invoke(obj,setter.getName(),value);
					} catch (Exception e) {
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();
			return obj;
		}
		return obj;
	}
	 @SuppressWarnings({ "unchecked", "rawtypes" })
	public static Map convertBeanToMapByRefAsm(Object bean) {
		 Map returnMap =null; 
		 try{
	        Class<?> type = bean.getClass(); 
	        BeanInfo beanInfo = Introspector.getBeanInfo(type); 
	        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors(); 
	    	MethodAccess access = getFromBeanMethodcache(type);
	        returnMap=new HashMap(); 
	    	for (PropertyDescriptor property  : propertyDescriptors) {
	    		String properName = property.getName();
	    		 Method getter = property.getReadMethod();  
	    		 try{
	    			 Object value=access.invoke(bean,getter.getName());
	    			 returnMap.put(properName, value); 
	    		 } catch(Exception e){}	
	    	}
		 }catch(Exception e){
			 e.printStackTrace();
		 }
	        return returnMap; 
	}
	public static Map<String,Object>  convertBeanToMap(Object bean) {
		 Map<String,Object> returnMap =null; 
		 try{
	        Class<?> type = bean.getClass(); 
	        BeanInfo beanInfo = Introspector.getBeanInfo(type); 
	        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors(); 
	        returnMap=new HashMap<String,Object> (); 
	    	for (PropertyDescriptor property  : propertyDescriptors) {
	    		String properName = property.getName();
	    		if("class".equals(properName)){
	    			continue;
	    		}
	    		 Method getter = property.getReadMethod();  
	    		 try{
		    		 Object value = getter.invoke(bean);  
		    		 returnMap.put(properName, value);  
	    		 }catch(Exception e){}	
	    	}
		 }catch(Exception e){
			 e.printStackTrace();
		 }
	        return returnMap; 
	}
	 
	 public static void unionDataSet(DataSet source,DataSet toBeUnion){
		 if(source==null||toBeUnion==null){
			return ;
		 }
		 source.getRows().addAll(toBeUnion.getRows());
		 source.getFooter().addAll(toBeUnion.getFooter());
		 
		 long a=source.getTotal();
		 long b=toBeUnion.getTotal();
		 source.setTotal(new Long(a+b));
		 source.setSimpleFooter(source.getSimpleFooter()+""+toBeUnion.getSimpleFooter());
		 
	 }
		private	static ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
		private static Validator validator = factory.getValidator();
		
		public static void validateBean(Object bean){
			if(bean==null){ZAlert.Error("参数错误");}
		        Class<?> type = bean.getClass(); 
		        BeanInfo beanInfo=null;
				try {
					beanInfo = Introspector.getBeanInfo(type);
				} catch (IntrospectionException e) {
					//e.printStackTrace();
				} 
//				Set<?> validateResx=validator.validate(bean);
//				for(Object validateResult:validateResx){
//		    		String message=((ConstraintViolation<?>)validateResult).getMessage();
//		    		ZAlert.Error(message);
//	    		}
		        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors(); 
		    	for (PropertyDescriptor property  : propertyDescriptors) {
		    		String properName = property.getName();
		    		if("class".equals(properName)){
		    			continue;
		    		}
		    		Set<?> validateRes=validator.validateProperty(bean,properName);
		    		if(validateRes==null||validateRes.isEmpty()){
		    			continue;
		    		}
		    		for(Object validateResult:validateRes){
			    		String message=((ConstraintViolation<?>)validateResult).getMessage();
			    		ZAlert.Error(message);
		    		}
		    		
		    	}
		}
		 
}
